home *** CD-ROM | disk | FTP | other *** search
/ Software Vault: The Diamond Collection / The Diamond Collection (Software Vault)(Digital Impact).ISO / cdr05 / wet101.zip / VB-DLL.TXT < prev    next >
Text File  |  1994-06-11  |  18KB  |  493 lines

  1. ----------------------------------------------------------------------
  2. | The following information was provided from Microsoft's Knowledge  |
  3. | Base and details how to write and use DLLs with Visual Basic.      |
  4. | This information is provided to you free of charge.  All credit    |
  5. | for this document belongs to Microsoft.                            |
  6. ----------------------------------------------------------------------
  7.  
  8. Knowledge Base
  9. Title: How to Write C DLLs and Call Them from Visual Basic 
  10. Document Number: Q106553           Publ Date: 29-MAR-1994 
  11. Product Name: Microsoft Visual Basic 
  12. Product Version:  3.00 
  13. Operating System: WINDOWS 
  14.  
  15.  --------------------------------------------------------------------- 
  16.  The information in this article applies to:
  17.  
  18.  - Standard and Professional Editions of Microsoft Visual Basic 
  19.    for Windows, version 3.0 
  20.  ---------------------------------------------------------------------
  21.  
  22.  SUMMARY 
  23.  =======
  24.  
  25.  This article outlines how to use DLLs with Visual Basic. It covers the 
  26.  following issues:
  27.  
  28.  Section A 
  29.  ---------
  30.  
  31.  1.0 What Is a DLL 
  32.  1.1 Why Use a DLL 
  33.  1.2 Anatomy of a DLL 
  34.  1.3 DLL Memory Management Issues 
  35.  1.4 Building a DLL Using Visual C++ 
  36.  1.5 Example C DLL
  37.  
  38.  Section B 
  39.  ---------
  40.  
  41.  2.0 Calling DLLs from Visual Basic 
  42.  2.1 DLL Parameters 
  43.  2.2 Trouble Shooting 
  44.  2.3 Example Visual Basic Calling Program
  45.  
  46.  MORE INFORMATION 
  47.  ==================
  48.  
  49.  SECTION A 
  50.  ---------
  51.  
  52.  1.0 What Is a DLL 
  53.  -----------------
  54.  
  55.  DLLs (Dynamic Link Libraries) are an important aspect of Windows. A DLL 
  56.  contains functions that your executable program can call during execution. 
  57.  In other words, a DLL is a library of functions that your program can link 
  58.  with dynamically.
  59.  
  60.  A link can be static or dynamic. Static links don't change. All the address 
  61.  information needed by your program to access the library function is fixed 
  62.  when the executable file is created and remains unchanged during execution.
  63.  
  64.  Dynamic links are created as needed. When your program needs a function 
  65.  that is not in the executable file, Windows loads the dynamic link library 
  66.  (the DLL), making all of its functions available to your application. At 
  67.  that time, Windows resolves the address of each function and dynamically 
  68.  links it to your application.
  69.  
  70.  All Custom controls used in Visual Basic are DLLs. The only difference is 
  71.  that they require special handling in terms of messages received from 
  72.  Visual Basic.
  73.  
  74.  1.1 Why Use DLLs 
  75.  ----------------
  76.  
  77.  Here are four reasons why you might want to use a DLL:
  78.  
  79.   - Access to C Run-Time Functions:
  80.     The C run-time library has many useful functions that would not be 
  81.     available to Visual Basic programmers were it not for DLLs. For example, 
  82.     the _dos_getdiskfree function allows you to calculate the total amount 
  83.     of disk space and the free disk space available on a drive.
  84.  
  85.   - Access to Windows API (Application Programming Interface) Functions that 
  86.     Require Callback Routines:
  87.  
  88.     Some Windows API functions require a callback function. A callback 
  89.     function is a function that Windows will call while executing the API 
  90.     call. An example of this sort of function is EnumTaskWindows, which 
  91.     will give the handle of all windows that are owned by a particular task.
  92.  
  93.   - Speed:
  94.  
  95.     C is a fully compiled language that works at a level that is fairly 
  96.     close to native machine code. This means that the execution of programs 
  97.     that are well written in C will be fast.
  98.  
  99.   - Load on Use:
  100.  
  101.     Code and data from a DLL are loaded only when needed. A DLL can be 
  102.     organized such that only required parts are loaded as opposed to the 
  103.     entire DLL. This reduces the amount of memory required and the time 
  104.     taken to load.
  105.  
  106.  1.2 Anatomy of a DLL 
  107.  --------------------
  108.  
  109.  Every DLL must contain a LibMain function and should contain a Windows Exit 
  110.  Procedure (WEP) in addition to the exported functions that can be called by 
  111.  an executable program.
  112.  
  113.   - LibMain:
  114.  
  115.     A DLL must contain the LibMain function. The LibMain function is called 
  116.     by the system to initialize the DLL. LibMain is called only once -- when 
  117.     the first program that requires the DLL is loaded. The following are the 
  118.     parameters passed to LibMain:
  119.  
  120.      - HANDLE : Handle to the instance of the DLL. 
  121.      - WORD   : Library's data segment. 
  122.      - WORD   : Heap size. 
  123.      - LPSTR  : Command line parameters.
  124.  
  125.   - WEP:
  126.  
  127.     The WEP (Windows Exit Procedure) performs cleanup for a DLL before the 
  128.     library is unloaded. Although a WEP function was required for every DLL 
  129.     in previous versions of the Windows operating system, for version 3.1 it 
  130.     is optional. A WEP should be included in the module definition file 
  131.     (.DEF) in Visual C, for example:
  132.  
  133.     EXPORTS 
  134.        WEP
  135.  
  136.   - Exported Functions:
  137.  
  138.     These are the functions you want to call from your DLL. They are denoted 
  139.     by _export. _export is used for backward compatibility. All the 
  140.     functions you want to call must also be listed in the (.DEF) file of 
  141.     your DLL.
  142.  
  143.  1.3 DLL Memory management issues 
  144.  ----------------------------------------------
  145.  
  146.  Use the large memory model.
  147.  
  148.  C stores all variables defined as static or global (defined outside of a 
  149.  function) in the program's heap space, and C stores all other variables on 
  150.  the stack.
  151.  
  152.  In the small and medium model, all pointers are near by default. This means 
  153.  that the data is accessed by 16-bit offsets to either the data segment (DS) 
  154.  register, or the stack segment (SS) register. Unfortunately, the compiler 
  155.  has no way of knowing whether the offset is from the DS or the SS. In most 
  156.  programs this would not be a problem because the DS and SS point to the 
  157.  same segment. A DLL, however, is a special case.
  158.  
  159.  A DLL has its own data segment but shares its stack with the calling 
  160.  program. This means that the DS and the SS do not point to the same 
  161.  location. The easiest solution to this problem is to build the DLL in the 
  162.  large memory model where all variables are referenced by a 32-bit value.
  163.  
  164.  Why Allocate Memory Dynamically? 
  165.  --------------------------------
  166.  
  167.  Allocating memory dynamically is a Windows-friendly technique. Declaring 
  168.  large arrays of data takes up space in either your program's stack, which 
  169.  is limited to 64K, or you program's Data Segment, which wastes disk space 
  170.  and Windows memory. It is better to ask  Windows for the memory when you 
  171.  need it, and then free it when you have finished.
  172.  
  173.  Allocating Memory 
  174.  -----------------
  175.  
  176.  In Windows, you can dynamically allocate two types of memory, local and 
  177.  global. Local memory is limited to 64K, and in the case of a DLL, local 
  178.  memory is shared with the program that called the DLL. Global memory is 
  179.  all of the memory available to Windows after it has loaded.
  180.  
  181.  Local memory is allocated and managed using the LocalAlloc, LocalLock 
  182.  LocalUnlock, and LocalFree functions -- as in this example:
  183.  
  184.     char* pszBuffer; 
  185.     .... 
  186.     pszBuffer = (char *) LocalAlloc (LPTR, 20); 
  187.     ... 
  188.     LocalFree (pszBuffer);
  189.  
  190.  It is faster to allocate local memory than it is to allocate global memory. 
  191.  But allocations from the local heap are limited to 64K, which must be 
  192.  shared amongst all programs that are calling the DLL. It is best to use 
  193.  local memory when small, short lived blocks of memory are required.
  194.  
  195.  Global memory is allocated and managed using the GlobalAlloc, GlobalLock 
  196.  GlobalUnlock, and GlobalFree functions -- as in this example:
  197.  
  198.     HGLOBAL hglb; 
  199.     char* pszBuffer;
  200.  
  201.     hglb = GlobalAlloc (GHND, 2048); 
  202.        // GHND allocates the memory as moveable and 
  203.        // initialized to 0 
  204.        // 2048 is the amount of memory to be allocated... 
  205.     pszBuffer = GlobalLock (hglb); 
  206.     ... 
  207.     GlobalUnlock (hglb); 
  208.     GlobalFree (hglb);
  209.  
  210.  The GlobalAlloc function allocates memory in multiples of 4K.
  211.  
  212.  If you want to share memory allocated in the DLL with other programs, you 
  213.  should allocate it using the GMEM_SHARED flag. If you want to share the 
  214.  memory through DDE, you must allocate it by using the GMEM_DDESHARE flag.
  215.  
  216.  Be Careful When Storing Data in Static Variables 
  217.  ------------------------------------------------
  218.  
  219.  If you try to store data in a DLL using global or static variables, don't 
  220.  be surprised if these values have changed when you next call your DLL. The 
  221.  data stored in this way will be common to all applications that access this 
  222.  DLL. No matter how many applications use a DLL, there is only one instance 
  223.  of the DLL. The best way to get around this is to return structures from 
  224.  the DLL and pass them in again when they are needed.
  225.  
  226.  File Handles 
  227.  ------------
  228.  
  229.  It is not possible to share file handles between applications or DLLs. Each 
  230.  application has its own file-handle table. For two applications to use the 
  231.  same file using a DLL, they must both open the file individually.
  232.  
  233.  1.4 Building a DLL Using Visual C++ 
  234.  -----------------------------------
  235.  
  236.  Here are the steps necessary to build a DLL using Visual C++:
  237.  
  238.  1. Start Visual C++.
  239.  
  240.  2. Create a new project by choosing New from the Project menu. Select the 
  241.     following options:
  242.  
  243.      - Set the Project Type to "Windows dynamic-link library (.DLL)" 
  244.      - Clear the "Use Microsoft Foundation Classes" check box.
  245.  
  246.     You can also set or view these options later by choosing Project from 
  247.     the Options menu.
  248.  
  249.  3. Add your existing .C and .DEF files to the project by using the dialog 
  250.     box that comes up when you choose Edit from the Project menu. Or enter 
  251.     your code directly in the Visual C++ editing window. (See the .C and 
  252.     .DEF example code listed below.)
  253.  
  254.  4. From the Project menu, choose the Build <yourname>.DLL option.
  255.  
  256.  1.5 Example C DLL 
  257.  -----------------
  258.  
  259.  The following DLL contains the GetDiskInfo function, which can be called 
  260.  from Visual Basic. It will return the disk space available, the current 
  261.  drive name and the volume name.
  262.  
  263.  C Code Example, DISKINFO.C:
  264.  
  265.  #include <windows.h> 
  266.  #include <dos.h>
  267.  
  268.  int CALLBACK LibMain (HANDLE hInstance, WORD wDataSeg, WORD wHeapSize, 
  269.  LPSTR lpszCmdLine) 
  270.  { 
  271.     if (wHeapSize > 0) 
  272.        UnlockData (0);  //Unlocks the data segment of the library. 
  273.     return 1; 
  274.  }
  275.  
  276.  void __export CALLBACK GetDiskInfo (char *cDrive, char *szVolumeName, 
  277.  unsigned long *ulFreeSpace) 
  278.  { 
  279.     unsigned drive; 
  280.     struct _diskfree_t driveinfo; 
  281.     struct _find_t c_file;
  282.  
  283.     _dos_getdrive (&drive); 
  284.     _dos_getdiskfree( drive, &driveinfo );
  285.  
  286.     if (!_dos_findfirst( "*.*", _A_VOLID, &c_file )) 
  287.        wsprintf( szVolumeName, "%s", c_file.name); 
  288.     else 
  289.        wsprintf ( szVolumeName, "NO LABEL");
  290.  
  291.     *cDrive = drive + 'A' -1;
  292.  
  293.     *ulFreeSpace = (unsigned long) driveinfo.avail_clusters * (unsigned 
  294.        long) driveinfo.sectors_per_cluster * (unsigned long) 
  295.        driveinfo.bytes_per_sector; 
  296.  }
  297.  
  298.  Use the following DISKINFO.DEF file in Visual C++:
  299.  
  300.     LIBRARY  diskinfo 
  301.     DESCRIPTION 'GetDiskInfo Can be called from Visual Basic' 
  302.     EXETYPE WINDOWS 3.1 
  303.     CODE PRELOAD MOVEABLE DISCARDABLE 
  304.     DATA PRELOAD MOVEABLE SINGLE 
  305.     HEAPSIZE    4096 
  306.     EXPORTS 
  307.        GetDiskInfo @1
  308.  
  309.  NOTE: The LIBRARY name in the .DEF file must be the same as the DLL file 
  310.  name, or else Visual Basic will give you "Error in loading DLL." For 
  311.  example, create the file DISKINFO.DLL using the LIBRARY DISKINFO statement 
  312.  in the .DEF file above.
  313.  
  314.  SECTION B 
  315.  ---------
  316.  
  317.  2.0 Calling DLLs from Visual Basic 
  318.  ----------------------------------
  319.  
  320.  In Visual Basic, all functions, including DLL functions, that you want to 
  321.  call must first be declared by using the Declare statement. You can declare 
  322.  your functions in the declarations section of a Form or a Module. If you 
  323.  declare a DLL procedure or function in a Form, it is private to that Form. 
  324.  To make it public, you must declare it in a Module. The following is an 
  325.  example Declare statement:
  326.  
  327.     Declare Sub getdiskinfo Lib "c:\somepath\diskinfo.dll" 
  328.        (ByVal mydrive As String, ByVal myvolume As String, free As Long)
  329.  
  330.  You must enter the entire Declare statement as one, single line. This 
  331.  particular Declare statement declares the user-defined procedure 
  332.  GETDISKINFO located in user-created DISKINFO.DLL file.
  333.  
  334.  Once you declare the function, you can call and use the function just as 
  335.  you would call and use a Visual Basic function.
  336.  
  337.  2.1 DLL Parameters 
  338.  ------------------
  339.  
  340.  Because DLLs are typically written in C, DLLs can use a wide variety of 
  341.  parameters not directly supported by Visual Basic. As a result, when 
  342.  passing parameters, he programmer has to find the appropriate data type to 
  343.  pass.
  344.  
  345.  Passing Arguments by Value or by Reference 
  346.  ------------------------------------------
  347.  
  348.  By default, Visual Basic passes all arguments by reference. (When passing 
  349.  by reference, Visual Basic supplies a 32-bit far address.) However, many 
  350.  DLL functions expect an argument to be passed by value. This can be 
  351.  achieved by placing the ByVal keyword in front of the argument declaration.
  352.  
  353.  The following sections show you how to convert parameters to Visual Basic.
  354.  
  355.  8- to 16-Bit Numeric Parameters 
  356.  -------------------------------
  357.  
  358.  Pass 8- to 16-bit numeric parameters (int, short, unsigned int, unsigned 
  359.  short, BOOL, and WORD) as Integer.
  360.  
  361.  32-bit Numeric Parameters 
  362.  -------------------------
  363.  
  364.  Pass 32-bit numeric parameters (long, unsigned long, and DWORD) as LONG.
  365.  
  366.  Object Handles 
  367.  --------------
  368.  
  369.  All handles are unique 16-bit integer values associated with a Window and 
  370.  are passed by value, so pass these parameters as Integer.
  371.  
  372.  Strings 
  373.  -------
  374.  
  375.  Strings include the LPSTR and LPBYTE data types (pointer to characters or 
  376.  pointer to unsigned characters). Pass these parameters as (ByVal paramname 
  377.  As String). DLL functions cannot return Visual Basic strings. They do 
  378.  sometimes return LPSTRs, which can be copied into Visual Basic strings 
  379.  by using API functions.
  380.  
  381.  To pass Visual Basic strings directly, pass them as (param As String).
  382.  
  383.  NOTE: Visual Basic strings require special handling, so don't pass strings 
  384.  directly unless the DLL explicitly requires it.
  385.  
  386.  Pointers to Numeric Values 
  387.  --------------------------
  388.  
  389.  Pass pointers to numeric values by simply not using the ByVal keyword. 
  390.  Structures 
  391.  ----------
  392.  
  393.  If the Visual Basic user-defined type matches the structure expected by the 
  394.  DLL, the structure can be passed by reference.
  395.  
  396.  NOTE: Structures cannot be passed by value.
  397.  
  398.  Pointers to Arrays 
  399.  ------------------
  400.  
  401.  Pass the first element of the array by reference.
  402.  
  403.  Pointers to functions 
  404.  ---------------------
  405.  
  406.  Visual Basic does not support callback functions, so DLL functions that 
  407.  have pointers to functions cannot be used with Visual Basic.
  408.  
  409.  Null Pointers 
  410.  -------------
  411.  
  412.  If a DLL expects a Null pointer, pass it as (ByVal paramname As Any). You 
  413.  can use &0 or &0H as the value of paramname.
  414.  
  415.  2.2 Trouble Shooting 
  416.  --------------------
  417.  
  418.  Below are solutions to some problems you may encounter.
  419.  
  420.  System Resources Keep Getting Lower After the DLL Is Called 
  421.  -----------------------------------------------------------
  422.  
  423.  If your DLL is using GDI objects, you must remember to free them after 
  424.  using them. This may not be obvious in Visual Basic, but when using the 
  425.  Windows SDK (software development kit) if you create a GDI object (for 
  426.  example, CreateBrushIndirect), you must delete it by using DeleteObject 
  427.  later on.
  428.  
  429.  Bad DLL Calling Convention Error 
  430.  -------------------------------- 
  431.  This error is often caused by incorrectly omitting or including the ByVal 
  432.  keyword from the Declare statement. This error can also be caused if the 
  433.  wrong parameters are passed.
  434.  
  435.  Error in loading DLL 
  436.  --------------------
  437.  
  438.  This error occurs when you call a dynamic-link library procedure and the 
  439.  file specified in the procedure's Declare statement cannot be loaded. You 
  440.  can use the Microsoft Windows API function LoadLibrary to find out more 
  441.  specific information about why a DLL fails to load.
  442.  
  443.  General Protection (GP) Fault 
  444.  -----------------------------
  445.  
  446.  GP faults occur when your program writes to a block of memory that doesn't 
  447.  belong to it. The two most likely reasons for this are:
  448.  
  449.   - You overstepped an array boundary. C does not check that the array 
  450.     subscript you are writing to is valid. Therefore, you can easily write 
  451.     to memory you don't own.
  452.  
  453.   - You are using a pointer to a memory location that you have freed. The 
  454.     best option is to assign NULL to all pointers after you free their 
  455.     memory.
  456.  
  457.  A GP fault can also occur when an incorrect variable type is passed to the 
  458.  DLL function.
  459.  
  460.  2.3 Example Visual Basic Calling Program 
  461.  ---------------------------------------- 
  462.  There are two parts to calling a DLL in a Visual Basic program. First you 
  463.  declare the function, and then you use it in event code.
  464.  
  465.  Here is an example of a Declare statement. The Declare statement should be 
  466.  put in a module or in a form's General Declarations section.
  467.  
  468.     ' Enter the following Declare as one, single line: 
  469.     Declare Sub getdiskinfo Lib "c:\dllartic\diskinfo.dll" 
  470.        (ByVal mydrive As String, ByVal myvolume As String, free As Long)
  471.  
  472.  Specify ByVal statements exactly as shown, or else a GP fault may occur.
  473.  
  474.  Once the function is declared, you can use it in event code. The following 
  475.  example uses a function from the DLL in the Command1 Click event code:
  476.  
  477.  Sub Command1_Click () 
  478.     Dim drive As String * 1 
  479.     Dim volume As String * 20 
  480.     Dim free As Long 
  481.     Call getdiskinfo(drive, volume, free) 
  482.     Text1.Text = drive 
  483.     Text2.Text = volume 
  484.     Text3.Text = Str$(free) 
  485.  End Sub
  486.  
  487.  Additional reference words: 3.00 
  488.  KBCategory: 
  489.  KBSubCategory: APrgOther RefsDoc
  490.  
  491.  
  492. COPYRIGHT Microsoft Corporation, 1994.
  493.